//
// Created by vivi on 07/02/2018.
//

#include "core/hairgeo/hairgeo.h"
#include "core/util/stringutil.h"
#include "core/util/fileutil.h"
#include "core/util/eigenutil.h"
#include <vector>
#include <algorithm>

namespace HairEngine {

	void StrandBasedHairGeometry::loadFromStream(std::istream &is, const Eigen::Affine3f &affine) {
		assert(is.good());

		//Read the data from the hair files
		nparticle = (size_t)FileUtility::binaryReadInt32(is);
		Assert(is);

		particles = new Eigen::Vector3f [nparticle];
		for (size_t i = 0; i < nparticle; ++i) {
			particles[i] = affine * FileUtility::binaryReadVector3f(is);
			Assert(is);
		}

		nstrand = (size_t)FileUtility::binaryReadInt32(is);
		Assert(is);

		endIndex = new size_t[nstrand];
		for (size_t i = 0; i < nstrand; ++i) {
			endIndex[i] = (i != 0 ? endIndex[i - 1] : 0) + (size_t) FileUtility::binaryReadInt32(is);
			Assert(is);
		}

		Info("Summary: \n");
		Info("Particle Size = %d\n", (int)nparticle);
		Info("Strand Size = %d\n", (int)nstrand);
		IF_ON_DEBUG {
			for (size_t i = 0; i < nstrand; ++i) {
				const Eigen::Vector3f * array = particleArrayOnStrand(i);
				const size_t size = particleSizeOnStrand(i);

				Info("Strand %d [%d] --> %s\n",
				     (int)i,
				     (int)particleSizeOnStrand(i),
				     EigenUtility::toString(array, array + size).c_str()
				);
			}
		};
	}

	StrandBasedHairGeometry::StrandBasedHairGeometry(const std::string &filePath, const Eigen::Affine3f &affine) {
		auto hairFile = std::fstream(filePath, std::ios::in | std::ios::binary);
		loadFromStream(hairFile, affine);
	}

	StrandBasedHairGeometry::StrandBasedHairGeometry(std::istream &is, const Eigen::Affine3f &affine) {
		loadFromStream(is, affine);
	}

	StrandBasedHairGeometry::StrandBasedHairGeometry(const StrandBasedHairGeometry &other):
			nstrand(other.nstrand),
			nparticle(other.nparticle),
			particles(new Eigen::Vector3f[other.nparticle]),
			endIndex(new size_t[other.nstrand]) {
		std::copy(other.particles, other.particles + other.nparticle, particles);
		std::copy(other.endIndex, other.endIndex + other.nstrand, endIndex);
	}

	StrandBasedHairGeometry::StrandBasedHairGeometry(StrandBasedHairGeometry &&other):
			nstrand(other.nstrand),
			nparticle(other.nparticle),
			particles(other.particles),
			endIndex(other.endIndex) {
		other.particles = nullptr;
		other.endIndex = nullptr;
	}


	StrandBasedHairGeometry::~StrandBasedHairGeometry() {
		SafeDeleteArray(particles);
		SafeDeleteArray(endIndex);
	}

	void StrandBasedHairGeometry::writeToFile(const std::string &filePath) const {
		std::fstream hairFile(filePath, std::ios::out | std::ios::binary);
		if (!hairFile.is_open())
			Error("Cannot open the file with the path %s", filePath.c_str());

		FileUtility::binaryWriteInt32(hairFile, (int32_t)nparticle);

		for (size_t i = 0; i < nparticle; ++i)
			FileUtility::binaryWriteVector3f(hairFile, particles[i]);

		FileUtility::binaryWriteInt32(hairFile, (int32_t)nstrand);
		for (size_t i = 0; i < nstrand; ++i)
			FileUtility::binaryWriteInt32(hairFile, (int32_t)particleSizeOnStrand(i));
	}
}